﻿namespace NetFx.MSIL
{
  using System;
  using System.Text;
  using System.Collections;

  using System.Reflection;
  using System.Reflection.Emit;
  using System.Runtime.InteropServices;

  using System.Diagnostics;
  using System.Diagnostics.CodeAnalysis;
  using System.Diagnostics.SymbolStore;

  using NetFx.Utils;

  /// <summary>
  /// 语义生成器.
  /// </summary>
  public interface SemanticGenerator
  {
    void OpenCatchBlock                 (Type exceptionType);
    void OpenExceptionBlock             (string label);
    void OpenExceptionFilterBlock       ();
    void OpenFaultBlock                 ();
    void OpenFinallyBlock               ();
    void CloseExceptionBlock            ();
    void ThrowException                 (Type excType);

    void OpenScope                      ();
    void CloseScope                     ();

    void DefineVariable                 (string name, Type type, bool pinned);
    void DefineLabel                    (string name);
    void MarkLabel                      (string name);
    void BoxAs                          (Type destType);
    void UnboxAs                        (Type destType);
    void CastAs                         (Type destType);
    void LoadConst                      (ValueType c);
    
    void ConvertTo                      (string primitiveType);
    void StoreElement                   (string elementType);
    void StoreElementRef();
    void LoadElement                    (string elementType);
    void LoadElementRef();
    void LoadElementAddress             (Type elementType);
    void GetArrayLength                 ();
    void Store                          (string name);
    void Load                           (string name);
    void LoadAddress                    (string name);
    void ConditionPrefix                (string condition);
    void Branch                         (string destlabel);
    void Branch                         (MethodInfo destmethod);
    void Breakpoint                     ();
    void Return                         ();
    void Branches                       (string[] labels);
    void Invoke                         (MemberInfo info);
    void VirtualInvoke                  (MemberInfo info);
    void InteropInvoke                  (Type returnType); //Need ReqParametersPrefix

    void ReqParametersPrefix            (params Type[] types);
    void OptParametersPrefix            (params Type[] types);
    void InteropConvertionPrefix        (string convention);
    void ManagedConvertionPrefix        (string convertion);
    void TestEquality                   ();
    void TestGreaterThan                ();
    void TestLessThan                   ();
    void TestInstanceOf                 (Type destType);
    void GetSizeOf                      (Type destType);
    void CheckInfinity                  ();
    void GetArgList                     ();
    void StoreArgument                  (string name);
    void LoadArgument                   (string name);
    void LoadArgumentAddress            (string name);
    void Execute                        (string op);
    void StoreIndirectRef               ();
    void StoreIndirect                  (string primitiveType);
    void LoadIndirectRef                ();
    void LoadIndirect                   (string primitiveType);
    void ThrowException                 ();
    void RethrowException               ();
    void LeaveException                 (string name);
    void EndExceptionFilter             ();
    void EndExceptionFinally            ();
    void TailPrefix                     ();
    void VolatilePrefix                 ();
    void ByteUnalignedPrefix            ();
    void ShortUnalignedPrefix           ();
    void IntUnalignedPrefix             ();
    void TopUnsignedPrefix              ();
    void TopUnsignedSuffix              ();
    void CheckingPrefix                 ();
    void CheckingSuffix                 ();
    void ShortFormatPrefix              ();
    void ShortFormatSuffix              ();

    void StoreStaticField               (FieldInfo field);
    void LoadStaticField                (FieldInfo field);
    void LoadStaticFieldAddress         (FieldInfo field);
    void StoreField                     (FieldInfo field);
    void LoadField                      (FieldInfo field);
    void LoadFieldAddress               (FieldInfo field);

    void CreateObjectOf                 (ConstructorInfo constructorInfo);
    void CreateArrayOf                  (Type type);
    void AllocateStack                  ();
    void InitializeMemory               ();
    void InitializeObject               (Type type);
    void CopyMemory();
    void CopyObject                     (Type type);
    void StoreObject                    (Type type);
    void LoadObject                     (Type type);
    void LoadNull                       ();
    void LoadString                     (string s);
    void LoadToken                      (MemberInfo member);
    void LoadFunction                   (MethodInfo method);
    void LoadVirtualFunction            (MethodInfo method);
    void CreateRefAny                   (Type type);
    void GetRefAnyType                  ();
    void GetRefAnyValue                 (Type type);
    void Idle                           ();
    void Discard                        ();
    void Duplicate                      ();
  }

  /// <summary>
  /// 
  /// </summary>
  public class SemanticLogicGenerator:LogicGenerator,SemanticGenerator
  {
    #region Classes
    #endregion
    #region Fields
    protected bool pinned = false;
    protected BranchCondition condition = BranchCondition.Always;
    protected IDictionary localVariables = new Hashtable();
    protected IDictionary argVariables = new Hashtable();
    protected IDictionary labels = new Hashtable();
    #endregion
    #region Properties
    protected virtual bool VariablePinned
    {
      get
      {
        return this.pinned;
      }
    }
    protected virtual IDictionary LocalVariables
    {
      get
      {
        return this.localVariables;
      }
    }
    protected virtual IDictionary ArgumentVariables
    {
      get
      {
        return this.argVariables;
      }
    }
    protected virtual IDictionary Labels
    {
      get
      {
        return this.labels;
      }
    }
    protected virtual BranchCondition Condition
    {
      get
      {
        return this.condition;
      }
    }
    #endregion
    #region Constructors
    public SemanticLogicGenerator(MethodBase reflectingBase):base(reflectingBase)
    {
    }
    #endregion
    #region Methods
    public virtual void OpenCatchBlock(Type expressionType)
    {
      this.Generator.BeginCatchBlock(expressionType);
    }
    public virtual void OpenExceptionFilterBlock()
    {
      this.Generator.BeginExceptFilterBlock();
    }
    public virtual Label OpenExceptionBlock()
    {
      return this.Generator.BeginExceptionBlock();
    }
    public virtual void OpenFaultBlock()
    {
      this.Generator.BeginFaultBlock();
    }
    public virtual void OpenFinallyBlock()
    {
      this.Generator.BeginFinallyBlock();
    }
    public virtual void CloseExceptionBlock()
    {
      this.Generator.EndExceptionBlock();
    }
    public virtual void ThrowException(Type exceptionType)
    {
      this.Generator.ThrowException(exceptionType);
    }

    public virtual void OpenScope()
    {
      this.Generator.BeginScope();
    }
    public virtual void CloseScope()
    {
      this.Generator.EndScope();
    }
    public virtual void DefineUsingNamespace(string usingNamespace)
    {
      this.Generator.UsingNamespace(usingNamespace);
    }
    public virtual LocalBuilder WriteLocalDeclaration(Type localType)
    {
      return this.Generator.DeclareLocal(localType);
    }
    public virtual LocalBuilder WriteLoaclDeclaration(Type localType, bool pinned)
    {
      return this.Generator.DeclareLocal(localType, pinned);
    }
    public virtual Label WriteLabelDeclaration()
    {
      return this.Generator.DefineLabel();
    }
    public virtual void WriteLabel(Label label)
    {
      this.Generator.MarkLabel(label);
    }
    public virtual void WritePositionMark(ISymbolDocumentWriter docWriter, Location startLocation, Location endLocation)
    {
      this.Generator.MarkSequencePoint(docWriter, startLocation.Row, startLocation.Column, endLocation.Row, endLocation.Column);
    }
    public virtual void OutputWriteLine(string value)
    {
      this.Generator.EmitWriteLine(value);
    }
    public virtual void OutputWriteLine(FieldInfo field)
    {
      this.Generator.EmitWriteLine(field);
    }
    public virtual void OutputWriteLine(LocalBuilder local)
    {
      this.Generator.EmitWriteLine(local);
    }

    public virtual void OpenExceptionBlock(string label)
    {
      this.NameLabel(this.OpenExceptionBlock(), label);
    }

    public virtual void ConvertTo(string primitiveType)
    {
      this.ConvertTo(this.GetPrimitiveTypeof(primitiveType));
    }
    public virtual void StoreElement(string elementType)
    {
      this.StoreElement(this.GetPrimitiveTypeof(elementType));
    }

    public virtual void LoadElement(string elementType)
    {
      this.StoreElement(this.GetPrimitiveTypeof(elementType));
    }
    public virtual void StoreIndirect(string primitiveType)
    {
      this.StoreIndirect(this.GetPrimitiveTypeof(primitiveType));
    }
    public virtual void LoadIndirect(string primitiveType)
    {
      this.LoadIndirect(this.GetPrimitiveTypeof(primitiveType));
    }

    public virtual void DefineVariable(string name)
    {
      if (this.ReqParameterTypes == null) throw new ArgumentException("can not determine variable type!\n are you missing ReqParameterTypes?");
      this.DefineVariable(name, this.ReqParameterTypes[0]);
      this.ReqParametersRestore();
    }
    public virtual void DefineLabel(string name)
    {
      this.Labels[name] = this.WriteLabelDeclaration();
    }
    public virtual void MarkLabel(string name)
    {
      this.WriteLabel(this.GetLabelOf(name));
    }

    public virtual void DefineVariable(string name, Type type)
    {
      this.DefineVariable(name, type, this.VariablePinned);
    }
    public virtual void DefineVariable(string name, Type type, bool pinned)
    {
      this.LocalVariables[name] = this.WriteLoaclDeclaration(type,pinned);
    }

    public virtual void Store(string name)
    {
      this.StoreAt(GetLocalVariableOf(name));
    }
    public virtual void Load(string name)
    {
      this.LoadAt(GetLocalVariableOf(name));
    }
    public virtual void LoadAddress(string name)
    {
      this.LoadAddressAt(this.GetLocalVariableOf(name));
    }

    public virtual void StoreArgument(string name)
    {
      this.StoreArgumentAt(this.GetParameterIndexOf(name));
    }
    public virtual void LoadArgument(string name)
    {
      this.LoadArgumentAt(this.GetParameterIndexOf(name));
    }
    public virtual void LoadArgumentAddress(string name)
    {
      this.LoadArgumentAddressAt(this.GetParameterIndexOf(name));
    }

    public virtual void Execute(string op)
    {
      this.Execute((BinaryOperation) Enum.Parse(typeof(BinaryOperation), op, true));
    }

    public virtual void LeaveException(string label)
    {
      this.LeaveException(this.GetLabelOf(label));
    }
    public virtual void Branch(string destLabel)
    {
      this.Branch(this.GetLabelOf(destLabel), this.Condition);
      this.ConditionRestore();
    }

    public virtual void Branches(params string[] labels)
    {
      Label[] rlabels = this.GetLabelsOf(labels);
      if (rlabels == null) throw new ArgumentNullException("labels can not be null!");
      this.Branches(rlabels);
    }

    public virtual void LoadToken(MemberInfo info)
    {
      if (info == null) throw new ArgumentNullException("info can not be null!");

      if (info is Type)
      {
        this.LoadToken((Type)info);
      }
      else if (info is MethodInfo)
      {
        this.LoadToken((MethodInfo)info);
      }
      else if (info is FieldInfo)
      {
        this.LoadToken((FieldInfo)info);
      }
      else
      {
        throw new ArgumentException("info of type " + info.GetType() + " is not supported!");
      }
    }

    public virtual void Invoke(MemberInfo info)
    {
      if (info == null) throw new ArgumentNullException("info can not be null!");
      if (info is MethodInfo)
      {
        this.Invoke((MethodInfo)info);
      }
      else if (info is ConstructorInfo)
      {
        this.Invoke((ConstructorInfo)info);
      }
      else
      {
        throw new ArgumentException("info of type " + info.GetType() + " can not be invoked!");
      }
    }
    public virtual void VirtualInvoke(MemberInfo info)
    {
      if (info == null) throw new ArgumentNullException("info can not be null!");
      if (info is MethodInfo)
      {
        this.VirtualInvoke((MethodInfo)info);
      }
      else if (info is ConstructorInfo)
      {
        this.VirtualInvoke((ConstructorInfo)info);
      }
      else
      {
        throw new ArgumentException("info of type " + info.GetType() + " can not be invoked!");
      }
    }
    #region Prefix and suffix
    public virtual void InteropConvertionPrefix(string convertion)
    {
      this.InteropConvertionPrefix((CallingConvention)Enum.Parse(typeof(CallingConvention), convertion,true));
    }
    public virtual void ManagedConvertionPrefix(string convertion)
    {
      this.ManagedConvertionPrefix((CallingConventions)Enum.Parse(typeof(CallingConventions), convertion,true));
    }
    public virtual void PinPrefix()
    {
      this.pinned = true;
    }
    public virtual void PinSuffix()
    {
      this.pinned = false;
    }
    public virtual void ConditionPrefix(string condition)
    {
      this.ConditionPrefix((BranchCondition)Enum.Parse(typeof(BranchCondition), condition,true));
    }
    public virtual void ConditionPrefix(BranchCondition condition)
    {
      this.condition = condition;
    }
    public virtual void ConditionRestore()
    {
      //Default is no-condition redirect.
      this.condition = BranchCondition.Always;
    }
    
    #endregion
    #region Utils
    protected virtual LocalBuilder GetLocalVariableOf(string name)
    {
      LocalBuilder local = this.LocalVariables[name] as LocalBuilder;
      if (local == null) throw new ArgumentException("no local has the name of " + name + " !");
      return local;
    }
    protected virtual Label[] GetLabelsOf(string[] names)
    {
      if (names != null)
      {
        Label[] labels = new Label[names.Length];
        for (int i = 0; i < labels.Length; i++)
        {
          labels[i] = this.GetLabelOf(names[i]);
        }
      }
      return null;
    }
    protected virtual Label GetLabelOf(string name)
    {
      if (this.Labels.Contains(name))
      {
        return (Label)this.Labels[name];
      }
      else
      {
        return this.NameLabel(this.WriteLabelDeclaration(), name);
        //throw new ArgumentException("no label has the name of " + name + " !");
      }
    }
    protected virtual Label NameLabel(Label label, string name)
    {
      return (Label)(this.Labels[name] = label);
    }
    protected virtual ushort GetParameterIndexOf(string name)
    {
      ParameterInfo[] ps = this.Parameters;
      if (ps != null)
      {
        for (ushort i = 0; i < ps.Length; i++)
        {
          if (ps[i].Name == name)
            return i;
        }
        throw new ArgumentException("no parameter has the name of " + name + " !");
      }
      throw new ArgumentException("no parameters at all!");
    }
    protected virtual PrimitiveType GetPrimitiveTypeof(string name)
    {
      return (PrimitiveType)Enum.Parse(typeof(PrimitiveType), name);
    }
    #endregion
    #endregion
    #region Utils
    protected override void Dispose(bool disposing)
    {
      if (disposing)
      {
        this.localVariables.Clear();
        this.labels.Clear();
        this.argVariables.Clear();
      }
      base.Dispose(disposing);
    }
    #endregion
  }
}
